'''
Created on Dec 5, 2010

@author: jackreilly
'''

from google.appengine.ext import db

from jettube.models import Station, User, getFromJetKey, Comment, Opinion
from datetime import timedelta, datetime
from random import shuffle
from jettube.server.utilities import YTID
import urllib2
from jettube.TPS.BeautifulSoup import BeautifulStoneSoup as bs
import logging
from jettube.models.User import getMasterUser

SONG_ID_KEY = 'songid'
LIKES_KEY = 'like'
DISLIKES_KEY = 'dislikes'
TITLE_KEY = 'title'
ENABLED_KEY = 'enabled'
USER_KEY = 'user'
STATION_KEY = 'station'
KEY_KEY = 'songKey'
COMMENT_KEY = 'commentsFromSongs'
OPINION_KEY = 'opinionsFromSongs'
YOUTUBE_SERVER_ERROR = 'SERVER_ERROR'
YOUTUBE_NO_EMBED = 'NO_EMBED'
VERIFY_OK = 'OK'
TOO_RECENT = 'TOO_RECENT'
RS_ID = 'id'
RS_PICURL = 'thumb'
RS_TITLE = 'title'

EXPIRATION_DAYS = 100
EXPIRATION_RATE = timedelta(EXPIRATION_DAYS)
NSONGSTOGRABFROM = 200
NSONGSPERBATCH = 30


class Song( db.Model ):
    songid = db.StringProperty()
    date = db.DateTimeProperty( auto_now_add = True )
    likes = db.IntegerProperty( default = 0 )
    dislikes = db.IntegerProperty( default = 0 )
    title = db.StringProperty()
    enabled = db.BooleanProperty( default = True )
    user = db.ReferenceProperty( User.User )
    station = db.ReferenceProperty( Station.Station )
    relatedSongs = db.ListProperty(item_type = db.Key)
    
    def getRelatedSongsDict(self):
        return [RelatedSong.get(key).toDict() for key in self.relatedSongs]
    
def addLikeDislikeToSongFromRequest(request):
    song = getSongFromRequest(request)
    like = getFromJetKey(request, LIKES_KEY)
    logging.error(like)
    logging.error('song: ' + str(song))
    if song:
        if like == 'true':
            song.likes+=1
        elif like == 'false':
            song.dislikes+=1
        song.put()
        
def putNewDebugSong(id,title):
    station = Station.getMasterStation()
    user = User.getMasterUser()
    Song(songid=id,station=station,user=user,title=title).put()

def putNewSongFromRequest(request):
    songid = getFromJetKey(request, SONG_ID_KEY)
    station = Station.getStationFromRequest(request)
    user = User.getUserFromRequest(request)
    if tooRecent(songid,station):
        return False
    try:
        newSong = Song()
        newSong.songid = songid
        newSong.user = user
        newSong.station = station
        newSong.title = getFromJetKey(request,TITLE_KEY)
        newSong.put()
        return newSong
    except:
        return False
    
def putRelatedSong(id,title,picURL):
    newSong = RelatedSong.get_or_insert(RelatedSong.createKeyName(id, title))
    newSong.id = id
    newSong.title = title
    newSong.picURL = picURL
    newSong.put()
    return newSong.key()
    
class RelatedSong(db.Model):
    id = db.StringProperty()
    title = db.StringProperty()
    picURL = db.StringProperty()
    date = db.DateTimeProperty(auto_now_add = True)
    
    @classmethod
    def createKeyName(cls,id,title):
        return '%s ::: %s' % (id,title)
    
    def toDict(self):
        return {RS_ID:self.id,
                RS_TITLE:self.title,
                RS_PICURL:self.picURL}

    
    
def verifySongWithYoutubeFromRequest(request):
    songid = getFromJetKey(request, SONG_ID_KEY)
    checkUrl = 'http://gdata.youtube.com/feeds/api/videos/%s?format=5&key=%s' % ( songid, YTID )
    try:
        xmlResponse = urllib2.urlopen( checkUrl ).read()
    except:
        return YOUTUBE_SERVER_ERROR
    if len( bs( xmlResponse ).findAll( 'yt:noembed' ) ) > 0:
        return YOUTUBE_NO_EMBED
    station = Station.getStationFromRequest(request)
    if tooRecent(songid, station):
        return TOO_RECENT
    return VERIFY_OK
    
def getSongsDictionaryFromRequest(request):
    songs = getSongsFromRequest(request)
    logging.error(songs)
    return [getSongDictionaryFromSong(song) for song in songs]
    
def getSongDictionaryFromSong(song):
    if song.user is None:
        song.user = User.getMasterUser()
    cm = {
             KEY_KEY:str(song.key()),
             SONG_ID_KEY:song.songid,
             TITLE_KEY: song.title,
             LIKES_KEY: song.likes,
             DISLIKES_KEY: song.dislikes,
             COMMENT_KEY: Comment.getCommentsDictionaryFromSongId(song.songid),
             OPINION_KEY: Opinion.getOpinionsDictionaryFromSongId(song.songid),
             USER_KEY: song.user.toDict()
             } 
    logging.error(cm)
    return cm
    
def getSongDictionaryFromRequest(request):
    song = Song.get(getFromJetKey(request, KEY_KEY))
    return getSongDictionaryFromSong(song)

def getSongFromRequest(request):
    songkey = getFromJetKey(request, KEY_KEY)
    return Song.get(songkey)
    
    
def getSongsFromRequest(request):
    station = Station.getStationFromRequest(request)
    if not station:
        station = Station.getMasterStation()
    songs = Song.all().filter('station',station).filter('enabled',True).fetch(NSONGSTOGRABFROM)
    filterAndShuffleSongs(songs)
    return songs[:NSONGSPERBATCH]
    
def filterAndShuffleSongs(songs):
    for song in songs:
        if songRejected(song):
            song.enabled = False
            song.put()
    filter(lambda(song): song.enabled, songs)
    shuffle(songs)
    
def songRejected(song):
    return songTooBad(song) or songTooOld(song)


def songTooOld(song):
    return song.date < datetime.now() - EXPIRATION_RATE

def songTooBad(song):
    return song.likes < song.dislikes - 2
        
def tooRecent(songid,station):
    return Song.all().filter('songid',songid).filter('station',station).filter('date >',datetime.now()  - EXPIRATION_RATE).count(limit=1) > 0